/*
HEADER:         CUG000.00;
TITLE:          SE (nee E) stack editor -- main();
DATE:           05/19/87;

DESCRIPTION:   "A full screen editor descended from Gilbert's 'e'(1981)
                through Haefner's qed and Tearle's ged.  Similar to
                Wordstar, with text stack, undo/redo, and other enhancements.
                Utilizes large RAM if available, virtual memory text storage
                in small systems.";

KEYWORDS:       Word processing, e, editor, qed, ged, full screen editor,
                text editor, program editor;

SYSTEM:         MS-DOS;
FILENAME:       SE.C;
SEE-ALSO:       CUG133, CUG133, CUG199;
AUTHORS:        G. Nigel Gilbert, James W. Haefner, Mel Tearle, G. Osborn;
COMPILERS:      Microsoft 4.00;
*/

/*   e/qed/ged screen editor

    (C) G. Nigel Gilbert, MICROLOGY, 1981
           licensed for private non-profitmaking use 1983
           August-December 1981

    Modified:   Aug-Dec   1984:  BDS-C 'e'(vers 4.6a) to 'qe' (J.W. Haefner)
                March     1985:  BDS-C 'qe' to DeSmet-C 'qed' (J.W. Haefner)
                May       1986:  converted to ged - Mel Tearle

    FUNCTIONS:  main, initialise, edit, finish, dispose, xit,
                askforfile, seldisk

    PURPOSE:    initialise; process commands;
*/

#include <stdio.h>
#include <ctype.h>
#include <setjmp.h>
#include "ged.h"

int cbreak;
int linel;
int pagingdisk;

main(argc,argv)
int argc;
char **argv;
{
    int argn;
    int i;
    char *dig;

/* --------------------- Default option settings ---------------------*/

    initjmp =    1;     /* jmp to line 1*/
    autoin  =  YES;     /* auto indent  [YES/NO]*/
    backup  =  YES;     /* make ".BAK" file [YES/NO]*/
    ctrl = NO;          /* Control characters stripped from input file */
    trail   =   NO;     /* leave trailing blanks [YES/NO]*/
    charmask =  0xFF;   /* allow input ASCII 0x80 to 0xFF */
    tabwidth    =   8;  /* tab stops every n cols [number]*/
    fullmem =  (long) RAMSLOTS*PAGESIZE/1024;
    displaypos  = YES;  /* display line:column at top of screen*/
    blockscroll = YES;  /* horizontal scroll whole page, not just current line*/
    eofchar = YES;      /* write ^Z end of file */
    pagingdisk  =   0;  /* create buffer file on this disk -
    set to either 0 (for currently logged-in disk)
    or to desired disk letter  (eg 'B') */

    defext[0] = '\0';   /* default extension (.c and .doc temporarilly hard coded*/

/* ------------------ End of default option settings -------------------- */

    inbufp = 0;
    rax = 0x30 << 8;
/* syscall is not a library function */
    syscall();

    if ( ( rax & 0x00ff ) < 2 )  {
        error1( "Must use MSDOS vers > 2.0" );
        exit(0);
    }

    filename[0] = name[0] = '\0';

    argn = 0;
    while ( ++argn < argc )
        if ( *argv[argn] == '-' )  {
            dig = argv[argn]+1;
            switch( toupper(*dig) )  {
            case 'A' :
                autoin=!autoin;
                break;
            case 'B' :
                backup=!backup;
                break;
            case 'C' :
                ctrl = !ctrl;
                break;
            case 'D' :
                pagingdisk = toupper(*(dig+1));
                if ( pagingdisk < 'A' || pagingdisk > 'Z' )
                    goto argerr;
                break;
            case 'H' :
                blockscroll=!blockscroll;
                break;

/* limit amount of RAM that will be allocated. number is in kb */
            case 'M' :
                i = 0;
                while (*++dig)
                    i = i*10 + *dig - '0';
                i = max(i, PAGESIZE * 3/1024);
                fullmem = min(fullmem, i);
                break;

            case 'P':
                charmask = 0x7F;   /* import Wordstar document mode */
                break;
            case 'T' :
                tabwidth=0;
                while (*++dig) tabwidth=tabwidth*10+*dig-'0';
                if (tabwidth < 1)
                    tabwidth = 1;
                break;
            case 'S' :
                trail=!trail;
                break;
            case 'Z' :
                eofchar = !eofchar;
                break;
            default  :
                if ( isdigit(*dig) )  {
                    initjmp = atoi((argv[argn]+1));
                    break;
                }
argerr:
                putstr("Illegal option: ");
                putstr(argv[argn]);
                exit(0);
            }
        }
        else {
            strcpy( filename[0] ? name : filename, argv[argn] );
        }
    ans[0] = patt[0] = opts[0] = '\0';

/* --------- start here ----------- */

/* remember state of ^C abort setting, then turn it off so that ^C can
 * be used for cursor control.
 */
    rax = 0x3300;
    syscall();
    cbreak = rdx & 0x00ff;
    rax = 0x3301;
    rdx = 0;
    syscall();

    keytranslate();
    do {
        initialise();
        edit();
    }
    while (YES);
}

/* ---------- end main ------------ */


initialise()
{
    int i, warn;

    lastl = cursorx = charn = offset = lastoff = 0;
    histptr = histcnt = ncommand = 0;

    cursory  = topline  = findir = jmpto = 1;
    blocking = isdim = repeat = NO;
    replace  = warn  = NO;
    blankedmess = YES;
    goteof  = YES;

    fbuf = &fbuf1;
    textbuf = &tbuf1;

    rax = 0x19 << 8;
    syscall();       /* get current disc */
    curdsk  = rax & 0xff;
    pagingfile[0] = (pagingdisk) ? pagingdisk : ( curdsk+'A' ) ;
    strcpy( pagingfile+1, ":e$$$.@@@" );

    initvm();

    text[0] = '\0';
    cline   = 1;
    inject(0,text);  /* create null line 1 */

    terminit();
    setstatusname();

    if ( filename[0] )  {
        curson(NO);
        cleareop(0);
        gotoxy(8,9);
        putstr("qed screen editor  version ");
        putstr(VERSION);
        putstr("  MICROLOGY 1983 and JWH 1985");
        gotoxy(17,10);
        putstr("ged 1.05 for Compaq, etc. - Mel Tearle 1986 ");
        gotoxy(17,11);
        putstr("Stack Editor version 1.00 G. Osborn 6-12-90");
        gotoxy(18,13);
        putstr("C Users' Group     Public Domain Software");
        gotoxy(0,21);
        putstr("F1 = help");

/* open a file, if it fails then try again to
 * open it using filename plus default extension
 * file handle is in textbuf structure.
 */
        while ( opentext(filename) == FAIL )  {
            askforfile();
            if ( !filename[0] )  goto  newfile;
        }
        lastl = 0;
        goteof = NO;

        if ( name[0] )  {
            strcpy(filename,name);
            name[0] = '\0';
        }
    }       /* end - if filename */

newfile:
    format( filename );

    for ( i = lastl; !(goteof); i += 1 )
        readtext(i);
    fclose(textbuf);
    setstatusname();
    gettext(1, charn);
    if ( initjmp> 2)  {              /* (2-1) is a bad jump from line 1 */
        jumpline(initjmp-cline);    /* not possible to do init jump to 2*/
        initjmp = 0;
    }
    linel = lastl;
}       /* end - initialise()  */


/* command processor */
edit()
{
    unsigned char inkey();
    unsigned char getkey();
    unsigned char  c;
    unsigned char  oldcrx, inc;
    int   i, j, k, to;
    jmp_buf env;   /* define data storage for setjmp (Microsoft) */

    setjmp(env);

    putstatusline(linel);  /* show the flie size until editing starts */
    displine = cline;
    blankedmess =  NO;
    calp();  /* initialization call */
    i = displaypos;
    displaypos = NO;
    putpage();
    displaypos = i;
    curson(YES);
    linem1 = 0;
    linem2 = 0;
    linem3 = 0;
    lastc = 0;

/* command processing loop */
nextchar:;
    goodline = cline;     /* restore environment */
    c = getkey();
    ncommand++;
    storehist = YES;

    switch(c)  {
    case UPPAGE :
    case DOWNPAGE :
        if (c == UPPAGE)
            movepage(-1);
        else
            movepage(0);
/* don't let input buffer overflow if auto repeat used */
        if (chkbuf() > 1)
            inbufp = 1;
        break;
    case DOWNKEY :  /* down one line */
        if (cline == lastl) {
            inject(cline,"");
            calp();  /* lastl has increased */
        }
/* a rewrite can be necessary if the cursor is moved into a marker.
 * Specify a preferred y cursor in that case */
        cursory += 1;
        moveline(1);
        break;
    case UPKEY :
        cursory -= 1;   /* preferred y cursor position */
        moveline(-1);
        break;
    case WINDDOWN :  /* window down one line */

        curson(NO);
        if (cursory == topline) {
            moveline(1);
        }
        if (pfirst != lastl) {
            cursory -= 1;
            scrollup(topline);
            i = plast;
            calp();
            if (i != lastl)
                putline(plast, SHEIGHT, NULL);
            resetpcursor();
        }
        curson(YES);
        break;

    case WINDUP : /* window up one line */

        curson(NO);
        if (cursory == SHEIGHT || pfirst == 1) {
            moveline(-1);
        }
        if (pfirst != 1) {
            cursory += 1;
            calp();
            scrolldown(topline);
            putline(pfirst, topline, NULL);
            resetpcursor();
        }
        curson(YES);
        break;

    case DELLNKEY :       /* delete from cursor to right margin */
        puttext();
/* add an undo step if the deleted line has been altered */
        ncommand++;
        text[charn] = '\0';
        if (charn == 0) {
            linedelete();
        }
        else {
            rewrite(charn);
            altered = YES;
        }
        break;

    case BLOCKKEY :
        switch ( to = blockops() )  {
        case YES :
            return;
            break;
        case 'x' :
        case 'q' :
            if ( finish(to) == YES )
                return;
            break;
        case 'r' :
            putmess(" |F|ile to read? ");
            if ( ( scans( name, FILELEN )) == ESCKEY )  break;
            if ( *(name) )  {
                readfile( name );
                putpage();
            }
            break;
        }
        break;
    case QWIKKEY :    /* all 'Q' functions */
        info();
        break;
    case BOLKEY :
        j = offset + (offset>0); /* leftmost visible character */
        if (j == charn)
            sync(j-SWIDTH+1);
        else
            sync(j);
        resetcursor();
        break;

    case EOLKEY :
        i = trim(text);          /* line end */
        k = offset + SWIDTH;     /* rightmost visible character */
        if (i > k+1)
            k--;         /* allow for field marker */

        j = k;
        if (j == charn || j-1 == charn) {
            j += SWIDTH-1;   /* overflow handled by sync() */
            if (i >= k && i < j && i != charn)
                j = i;
            sync(j);
        }
        else {
            if ( i < k && charn < i)
                sync(i);
            else
                sync(k);
        }
        resetcursor();
        break;
    case HOMEKEY :    /* last line of file */
/* jumpline is prefered to moveline here */
        cursory = SHEIGHT - (SHEIGHT - topline)/4;
        jumpline(lastl-cline);
        break;
    case BOFKEY :     /* first line of file */
/* no choice in cursory */
        jumpline(1-cline);
        break;
    case LEFTKEY :
        movechar(-1);
        break;
    case RIGHTKEY :
        movechar(1);
        break;
    case LEFTWKEY :
        moveword(-1);
        break;
    case RIGHTWKEY :
        moveword(1);
        break;
    case DELLEFT :
        deletechar(-1);
        break;
    case DELRIGHT :
        deletechar(0);
        break;
    case DELWDKEY :
        deleteword();
        break;
    case JUMPKEY :
        dojump();
        break;
    case TOPSCRKEY :
        moveline( (topline-cursory) ); /* no current key assignment */
        break;
    case BOTSCRKEY :
        moveline( (SHEIGHT-cursory) );
        break;
    case REPKEY :
        repeat = YES;
        if (replace)
            dofindrep(MAXINT,findir);
        else
            dofindrep(1,findir);
        repeat = NO;
        break;
    case HELPKEY :
        if ( topline > 1 )  {
            topline = 1;
            unmess();
        }
        else  {
            dohelp();
        }
        break;
    case CR :
        crinsert(0);
        break;
    case CRSTILL :        /* line break */
        crinsert(1);
        break;
    case OOPSKEY :
        undo();
        break;
    case POP :
        pop(0);
        break;
    case CPOP :
        pop(1);
        break;
    case TAB :
/* tab is a cursor positioning command in this version */
        i = tabwidth - (cursorx % tabwidth);   /* 1 <= i <= tabwidth */
        charn += i;
        sync(charn);
        resetcursor();
        break;

    case INSKEY :
        charep = 1 - charep;   /* toggle */
        putlineno(cline);   /* show the new header */
        break;

    case ESCKEY :
        if ( topline > 1 )
            unmess();
        blankedmess = YES;
        break;

    case F1KEY :                     /* help menu */
        if ( topline > 1 )
            unmess();
        else
            dohelp();
        break;

    case F6KEY:
/* center window on cursor.  will not move window right if right end of
   all lines already visible */
        cent();
        break;

    case F3KEY :
        if (replace)
            dofindrep(-1,-1);
        else
            dofindrep(1,-1);   /* backward ^L.  repeat string search or s & r*/
        break;

    case F4KEY :
        if (replace)
            dofindrep(-1,1);
        else
            dofindrep(1,1);    /* forward ^L */
        break;

    case F5KEY :
        break;

    case F2KEY :
        findorrep(0);   /* start string search.  */
        break;

    case F7KEY :   /* options */
        envir();
        break;


    case F8KEY :    /* disc directory */
        cleareol( 0, 0 );
        putmess( "Enter wildcard filespec, <ret> for all) ");
        c = dirmsk();
        if ( c != ESCKEY  &&  c != YES )  {
            gotoxy( 58, SHEIGHT );
            getkey();
        }
        cleareop(0);
        topline = 1;  /* help off if on */
        putpage();
        putstatusline( cline );
        break;

    case F9KEY :   /* exit without saving if no changes */
        puttext();
        finish('q');
        break;

    case F10KEY :       /* save and exit. change x to F10KEY for save & */
        puttext();
        finish('x');       /* stay.  that is ok for floppies but not fixed disc */
        break;

    default :
        if (charep)
            replchar(c);
        else
            insertchar(c);
        break;
    }

/* ensure that all modules maintain the correct vertical window boundary */
    i = pfirst;
    j = plast;
    calp();
    if (i != pfirst || j != plast )
        cerr(1);
/* insure that the status line is current */
    if ( displine != cline || dispcol != cursorx+1 )
        cerr(2);

/* End of command character processing.  Tidy up for next input.
 * Don't put up new status line if a command is waiting because it will
 * immediately be replaced.  ^K and ^Q cause excessive flashing in that case.
 */
    if ( blankedmess && (chkbuf() == 0) )
        putstatusline(cline);
    goto nextchar;
}


/* No to return to current file or don't return,
 * but exit if finished altogther
 * return Yes to edit another file
 */
finish(o)
int o;
{
    int abandon;
    char c, tempname[FILELEN], namebak[FILELEN], *strcpy();

    if (altered)
        cerr(3);    /* disc file not updated from RAM */
    c = '\0';

    if ( o == F10KEY && !filename[0] )  return  NO;

    if ( o != 'x' && o != F10KEY )  {
        if ( o != 'q' || lastc) {
            putmess( "|W|rite edited text to file, |A|bandon all edits, or |<esc>|? " );
            while ( (c = getlow() ) != 'w' && c != 'a' &&  c != ESCKEY);
            if ( c != ESCKEY)
                putch(c);
            else
                return  NO;
        }
        abandon = c == 'a';
    }

    if ( (c == 'w') || (o == 'x') || (o == F10KEY) )  {
        if ( (!filename[0]) && (o != F10KEY) )  {
            putmess("|F|ile to write to? ");
            if ( ( scans( filename, FILELEN )) == ESCKEY )  return NO;
            format(filename);
            if ( filename[0] <= ' '  ||  (!backup && !exists(filename)) )  {
                filename[0] == '\0';
                return  NO;
            }
        }
        if ( backup )  {   /* delete old bak file */
            retag(strcpy(namebak,filename),"BAK");
            if ( checkexists(namebak) )
                if ( funlink(namebak) == FAIL )  return  NO;
        }
/* keep old name in 'filename' */
        strcpy( tempname, filename );
/* new file called 'filename.@@@ ' - or whatever you like  */
        retag( tempname, "@@@" );
        if ( writefile(1,lastl,tempname,filename ) == FAIL )
            return  NO;
/* check original file still exists -
 * may have been deleted or renamed by user
 */
        if ( checkexists( filename ) )  {
            if ( backup )  {
/* orig. file becomes '.bak' */
                if ( frename( filename, namebak ) == FAIL )  goto failed;
            }
            else {
/* delete orig file */
                if ( funlink(filename) == FAIL )  {
failed:             /* if can't delete/rename old file, change
                    * new name to '.@@@'
                    */
                    strcpy( filename, tempname );
                    goto nowrite;
                }
            }
        }
/* new file goes from '@@@' to orig name */
        frename(tempname,filename);
        lastc = 0;     /* file now not changed */
    }
    if ( o == 'x' || o == F10KEY)  {
        if ( pagefd != NOFILE )  {
            close(pagefd);
            funlink(pagingfile);
        }
        xit();
    };

nowrite:
    if ( o == F10KEY )  goto qksave;
    if ( o != 'q' || lastc) {
        putmess("|E|xit to DOS, edit |A|nother file, or |<esc>|? ");
        while ( (c=getlow() ) != 'e' && c != ESCKEY && c != 'a' )
            ;
        if ( c != ESCKEY)
            putch(c);
    }
    else
        c = 'e';
    switch(c) {
    case 'e' :
        if ( lastc )
            if ( !dispose() )  return  NO;
/*      fclose(textbuf);  */
        if ( pagefd != NOFILE ) {
            close(pagefd);
            funlink(pagingfile);
        }
        xit();
    case 'a' :
        if ( lastc )
            if ( !dispose() )  return  NO;
/*      fclose(textbuf);  */
        if ( pagefd != NOFILE )  {
            close(pagefd);
            funlink(pagingfile);
        }
        askforfile();
        return  YES;
qksave:
    case ESCKEY :
        if ( !abandon )  {
            gotoxy( WAITPOS, 0 );
            opentext( filename );
            lastl    = 1;
            goteof   = NO;
            initvm();
            cleareol( 0, 0 );
            gettext( cline, charn);
            putstatusline( cline );
        }
        return  NO;
    }
}


/* check to dispose of modified file */
dispose()
{
    putmess( "|A|bandon modified file?| (y/n) ");
    if ( getlow() == 'y' )
        return  YES;
    else
        return  NO;
}


/* quit editor */
xit()
{
    cleareol( 0, 24 );
    gotoxy( 0, 23 );
    curson(YES);
    rax = 0x3301;
    rdx = cbreak;  /* restore ^C to its original state */
    syscall();
    exit( 0 );
}


/* get another file to edit into 'filename' */
askforfile()
{
    int drive;

    cleareop(1);
dirdone:;
    gotoxy(0,1);  /* line 0 may contain an error message here */
    putstr("|<return>| to create a new file\n" );   /* line 2 */
    putstr("|>|        to view directory|\n");      /* line 3 */
    putstr("|d:|       to change default disk\n");  /* line 4 */
    putstr("|<esc>|    to exit\n");                 /* line 5 */

    cleareol(0, 6);                                 /* line 6 */
    curson(YES);
    putstr("|File to edit:  " );

    if ( scans(filename,FILELEN) == ESCKEY )
        xit();

    cleareol(EMPOS,0);
    if ( filename[0] == '>' )  {
        filename[0] = '\0';
        cleareol(0,6);
        putstr( "Enter wildcard filespec, |<ret>| for all " );
        dirmsk();
        goto  dirdone;
    }

    if ( strlen(filename) == 2 &&  filename[1] == ':' )  {
        if ( ( drive = toupper( filename[0]) - 'A' ) >= 0 && drive < 26 )  {
            seldisk ( drive );
            curdsk = drive;
            goto dirdone;
        }
    }
    else  {
        name[0] = '\0';
        return  YES;
    }
}



seldisk(drive)
int drive;
{
    rax = 0x0E << 8;
    rdx = drive;
    syscall();  /* no error status returned */
    return 0;
}








